# -*- coding: utf-8 -*-
# =============================================================================
#     FileName:
#         Desc:
#       Author: GGA
#        Email:
#     HomePage:
#      Version: 1.0.1
#   LastChange: 2020-12-20
#      History:
# =============================================================================
import datetime
import queue
import threading
import time

import fire
import psutil
import redis
import termcolor


class RedisMonitor(object):
    def __init__(self, redis_host, redis_port, redis_auth, check_interval=1):
        self.redis_host = redis_host
        self.redis_port = redis_port
        self.redis_auth = redis_auth
        self.check_interval = check_interval
        self.redis_conn = None
        self.redis_configs = None
        self.prev_redis_info = None
        self.curr_redis_info = None
        self.cpu_data_queue = queue.Queue()
        self.used_cpu_rate = 0
        self.redis_pid = 0
        self._init_data()

    def monitor_redis_cpu(self):
        if self.redis_host != "127.0.0.1" and self.redis_host != "localhost":
            print("非本机监控,无法监控CPU使用率")
            return
        process = psutil.Process(self.redis_pid)
        while True:
            # 获取进程的CPU使用率
            cpu_usage = process.cpu_percent(interval=1)
            # print("put:: {cpu_usage}".format(cpu_usage=cpu_usage))
            self.cpu_data_queue.put(cpu_usage)

    def start_cpu_monitor(self):
        thread = threading.Thread(target=self.monitor_redis_cpu)
        thread.daemon = True
        thread.start()

    def update_used_cpu_rate(self):
        while True:
            try:
                if not self.cpu_data_queue.empty():
                    cpu_usage = self.cpu_data_queue.get()
                    # print("get:: {cpu_usage}".format(cpu_usage=cpu_usage))
                    self.used_cpu_rate = cpu_usage
                else:
                    return
            except Exception as _:
                return

    def _init_data(self):
        self.redis_conn = redis.StrictRedis(host=self.redis_host, port=self.redis_port, password=self.redis_auth, db=0)
        self.redis_configs = self.redis_conn.config_get("*")
        self.prev_redis_info = self.redis_conn.info()
        self.curr_redis_info = self.redis_conn.info()
        self.redis_pid = self.get_current_monitor_value("process_id")

    def get_accumulate_monitor_value(self, key_name, key_rate=1):
        return (self.curr_redis_info[key_name] - self.prev_redis_info[key_name]) * key_rate / self.check_interval

    def get_current_monitor_value(self, key_name, key_rate=1):
        return self.curr_redis_info[key_name] * key_rate

    def get_config_value(self, config_key):
        return self.redis_configs[config_key]

    def show_monitor_data(self, check_times):
        redis_qps = self.get_accumulate_monitor_value("total_commands_processed")
        used_memory_human = self.get_current_monitor_value("used_memory_human")
        max_memory = int(self.get_config_value("maxmemory"))
        used_memory = self.get_current_monitor_value("used_memory")
        max_memory_human = str(round(max_memory / 1024 / 1024 / 1024, 2)) + "G"
        if max_memory <= 0:
            used_memory_rate = 0
        else:
            used_memory_rate = round(used_memory * 100 / max_memory, 2)
        connected_clients = self.get_current_monitor_value("connected_clients")
        max_clients = int(self.get_config_value("maxclients"))
        connected_rate = round(connected_clients * 100 / max_clients, 2)
        input_mbps = round(self.get_current_monitor_value("instantaneous_input_kbps") * 8 / 1024.0, 2)
        output_mbps = round(self.get_current_monitor_value("instantaneous_output_kbps") * 8 / 1024.0, 2)
        print_info_temp = "{monitor_time:>20}{redis_qps:>15}{used_cpu:>15}" \
                          "{used_memory:>15}{max_memory:>15}{memory_rate:>15}" \
                          "{cur_connection:>15}{max_connection:>15}{connection_rate:>15}" \
                          "{input_mbps:>15}{output_mbps:>15}"
        if check_times % 20 == 0:
            print_info = print_info_temp.format(
                monitor_time="monitor_time",
                redis_qps="redis_qps",
                used_cpu="used_cpu",
                used_memory="used_memory",
                max_memory="max_memory",
                memory_rate="memory_rate",
                cur_connection="cur_conn",
                max_connection="max_conn",
                connection_rate="conn_rate",
                input_mbps="input_mbps",
                output_mbps="output_mbps"
            )
            print(termcolor.colored(print_info, "green"))
        print_info = print_info_temp.format(
            monitor_time=datetime.datetime.now().strftime("%H:%M:%S"),
            redis_qps=str(redis_qps),
            used_cpu="{:.2f}".format(self.used_cpu_rate) + "%",
            used_memory=used_memory_human,
            max_memory=max_memory_human,
            memory_rate="{:.2f}".format(used_memory_rate) + "%",
            cur_connection=str(connected_clients),
            max_connection=str(max_clients),
            connection_rate="{:.2f}".format(connected_rate) + "%",
            input_mbps="{:.2f}".format(input_mbps),
            output_mbps="{:.2f}".format(output_mbps)
        )
        if int(self.used_cpu_rate) > 60 or int(used_memory_rate) > 80 or int(connected_rate) > 60:
            print(termcolor.colored(print_info, "red"))
        else:
            print(termcolor.colored(print_info, "green"))

    def monitor_redis(self):
        check_times = 0
        self.start_cpu_monitor()
        self.prev_redis_info = self.redis_conn.info()
        while True:
            time.sleep(self.check_interval)
            self.curr_redis_info = self.redis_conn.info()
            self.update_used_cpu_rate()
            self.show_monitor_data(check_times)
            self.prev_redis_info = self.curr_redis_info
            check_times += 1
        pass


def main(redis_host="127.0.0.1", redis_port=6379, redis_auth="", check_interval=1):
    monitor = RedisMonitor(redis_host, redis_port, redis_auth, check_interval)
    monitor.monitor_redis()


if __name__ == '__main__':
    fire.Fire(main)
